package com.pearlsoft.rms.vcenter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.soap.SOAPFaultException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

import com.pearlsoft.rms.MetricResult;
import com.vmware.vim25.ArrayOfPerfInterval;
import com.vmware.vim25.DynamicProperty;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.ObjectSpec;
import com.vmware.vim25.PerfCounterInfo;
import com.vmware.vim25.PerfEntityMetric;
import com.vmware.vim25.PerfEntityMetricBase;
import com.vmware.vim25.PerfInterval;
import com.vmware.vim25.PerfMetricId;
import com.vmware.vim25.PerfMetricIntSeries;
import com.vmware.vim25.PerfMetricSeries;
import com.vmware.vim25.PerfQuerySpec;
import com.vmware.vim25.PerfSummaryType;
import com.vmware.vim25.PropertyFilterSpec;
import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.VimPortType;

/**
 * 
 * @author rainshow
 * @Created 2012-5-15 上午09:38:02
 */

@Service("history")
public class History extends VcenterClient {

    private static Log logger = LogFactory.getLog(History.class);

    /**
     * This method initializes all the performance counters available on the
     * system it is connected to. The performance counters are stored in the
     * hashmap counters with group.counter.rolluptype being the key and id being
     * the value.
     */
    private List<PerfInterval> getPerfInterval() {
        List<PerfInterval> pciArr = null;
        try {
            // Create Property Spec
            PropertySpec propertySpec = new PropertySpec();
            propertySpec.setAll(Boolean.FALSE);
            propertySpec.getPathSet().add("historicalInterval");
            propertySpec.setType("PerformanceManager");
            List<PropertySpec> propertySpecs = new ArrayList<PropertySpec>();
            propertySpecs.add(propertySpec);

            // Now create Object Spec
            ObjectSpec objectSpec = new ObjectSpec();
            objectSpec.setObj(getConnection().getPerfManager());

            List<ObjectSpec> objectSpecs = new ArrayList<ObjectSpec>();
            objectSpecs.add(objectSpec);

            // Create PropertyFilterSpec using the PropertySpec and ObjectPec
            // created above.
            PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec();
            propertyFilterSpec.getPropSet().add(propertySpec);
            propertyFilterSpec.getObjectSet().add(objectSpec);

            List<PropertyFilterSpec> propertyFilterSpecs = new ArrayList<PropertyFilterSpec>();
            propertyFilterSpecs.add(propertyFilterSpec);

            List<PropertyFilterSpec> listpfs = new ArrayList<PropertyFilterSpec>(1);
            listpfs.add(propertyFilterSpec);
            List<ObjectContent> listocont = retrievePropertiesAllObjects(listpfs);

            if (listocont != null) {
                for (ObjectContent oc : listocont) {
                    List<DynamicProperty> dps = oc.getPropSet();
                    if (dps != null) {
                        for (DynamicProperty dp : dps) {
                            List<PerfInterval> perintlist = ((ArrayOfPerfInterval) dp.getVal()).getPerfInterval();
                            pciArr = perintlist;
                        }
                    }
                }
            }
        } catch (SOAPFaultException sfe) {
            printSoapFaultException(sfe);
        } catch (Exception e) {
            logger.error(e);
        }
        return pciArr;
    }

    public MetricResult getHistory(String vmname, String groupname, String countername, int starttime, int duration,
            int interval) throws Exception {
        ManagedObjectReference vm = this.getVmByVMname(vmname);
        if (vm == null) {
            logger.error("VirtualMachine " + vmname + " not found");
            return new MetricResult();
        }
        counterInfo(getConnection().getPerfManager());
        List<PerfInterval> intervals = getPerfInterval();

        // Integer interval = new Integer(Integer.parseInt(interval));
        boolean valid = checkInterval(intervals, Integer.valueOf(interval));
        if (!valid) {
            logger.error("Invalid interval, Specify one from above");
            return new MetricResult();
        }

        PerfCounterInfo pci = getCounterInfo(groupname, countername, PerfSummaryType.AVERAGE, null);
        if (pci == null) {
            logger.error("Incorrect Group Name and Countername specified");
            return new MetricResult();
        }

        PerfQuerySpec qSpec = new PerfQuerySpec();
        qSpec.setEntity(vm);
        qSpec.setMaxSample(new Integer(10));
        List<PerfQuerySpec> qSpecs = new ArrayList<PerfQuerySpec>();
        qSpecs.add(qSpec);
        VimPortType vimPort = this.getConnection().getVimPort();
        XMLGregorianCalendar serverstarttime = vimPort.currentTime(VcenterConnection.SVC_INST_REF);
        XMLGregorianCalendar serverendtime = vimPort.currentTime(VcenterConnection.SVC_INST_REF);
        serverstarttime.setTimezone(8);
        serverendtime.setTimezone(8);
        Calendar start = serverstarttime.toGregorianCalendar();
        start.add(Calendar.MILLISECOND, -starttime * 60 * 1000);
        serverstarttime.setYear(start.get(Calendar.YEAR));
        serverstarttime.setMonth(start.get(Calendar.MONTH) + 1);
        serverstarttime.setDay(start.get(Calendar.DAY_OF_MONTH));
        serverstarttime
                .setTime(start.get(Calendar.HOUR_OF_DAY), start.get(Calendar.MINUTE), start.get(Calendar.SECOND));

        Calendar end = serverendtime.toGregorianCalendar();
        end.add(Calendar.MILLISECOND, -(starttime - duration) * 60 * 1000);
        serverendtime.setYear(end.get(Calendar.YEAR));
        serverendtime.setMonth(end.get(Calendar.MONTH) + 1);
        serverendtime.setDay(end.get(Calendar.DAY_OF_MONTH));
        serverendtime.setTime(end.get(Calendar.HOUR_OF_DAY), end.get(Calendar.MINUTE), end.get(Calendar.SECOND));
        logger.info("Start Time " + serverstarttime.toGregorianCalendar().getTime().toString());
        logger.info("End Time   " + serverendtime.toGregorianCalendar().getTime().toString());

        List<PerfMetricId> listprfmetid = vimPort.queryAvailablePerfMetric(getConnection().getPerfManager(), vm,
                serverstarttime, serverendtime, Integer.valueOf(interval));

        PerfMetricId ourCounter = null;

        for (int index = 0; index < listprfmetid.size(); ++index) {
            if (listprfmetid.get(index).getCounterId() == pci.getKey()) {
                ourCounter = listprfmetid.get(index);
                break;
            }
        }
        if (ourCounter == null) {
            logger.warn("No data on Host to collect. " + "Has it been running for at least " + duration + " minutes");
            return new MetricResult(pci.getUnitInfo().getSummary());
        } else {
            qSpec = new PerfQuerySpec();
            qSpec.setEntity(vm);
            qSpec.setStartTime(serverstarttime);
            qSpec.setEndTime(serverendtime);
            qSpec.getMetricId().addAll(Arrays.asList(new PerfMetricId[] { ourCounter }));
            qSpec.setIntervalId(Integer.valueOf(interval));
            qSpecs.add(qSpec);

            List<PerfQuerySpec> alpqs = new ArrayList<PerfQuerySpec>(1);
            alpqs.add(qSpec);
            List<PerfEntityMetricBase> listpemb = vimPort.queryPerf(getConnection().getPerfManager(), alpqs);

            if (listpemb != null) {
                return new MetricResult(pci.getUnitInfo().getSummary(), getValues(listpemb, pci, ourCounter, Integer
                        .valueOf(interval)));
            } else {
                logger.warn("No Samples Found");
                return new MetricResult();
            }
        }
    }

    private boolean checkInterval(List<PerfInterval> intervals, Integer interv) throws Exception {
        boolean flag = false;
        for (int i = 0; i < intervals.size(); ++i) {
            PerfInterval pi = intervals.get(i);
            if (pi.getSamplingPeriod() == interv) {
                flag = true;
                break;
            }
        }
        if (!flag) {
            logger.info("Available summary collection intervals");
            logger.info("Period\tLength\tName");
            for (int i = 0; i < intervals.size(); ++i) {
                PerfInterval pi = intervals.get(i);
                logger.info(pi.getSamplingPeriod() + "\t" + pi.getLength() + "\t" + pi.getName());
            }
        }
        return flag;
    }

    private List<List<Long>> getValues(List<PerfEntityMetricBase> values, PerfCounterInfo pci, PerfMetricId pmid,
            Integer inter) {
        List<List<Long>> data = new ArrayList<List<Long>>();
        for (int i = 0; i < values.size(); ++i) {
            List<PerfMetricSeries> listperfmetser = ((PerfEntityMetric) values.get(i)).getValue();
            for (int vi = 0; vi < listperfmetser.size(); ++vi) {
                if (pci != null) {
                    if (pci.getKey() != listperfmetser.get(vi).getId().getCounterId()) {
                        continue;
                    }
                }
                if (listperfmetser.get(vi) instanceof PerfMetricIntSeries) {
                    PerfMetricIntSeries val = (PerfMetricIntSeries) listperfmetser.get(vi);
                    List<Long> listlongs = val.getValue();
                    data.add(listlongs);
                }
            }
        }
        return data;
    }

    public static void main(String[] args) throws Throwable {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                new String[] { "classpath:applicationContext.xml" });
        History h = context.getBean("history", History.class);
        List<List<Long>> data = h.getHistory("AD", "mem", "usage", 240, 240, 300).getData();
        if (!data.isEmpty()) {
            System.out.println(data.get(0));
        }
    }
}